{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.5/dist-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n"
     ]
    }
   ],
   "source": [
    "# 导入模块\n",
    "from tensorflow.examples.tutorials.mnist import input_data\n",
    "import tensorflow as tf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting ./MNIST_data/train-images-idx3-ubyte.gz\n",
      "Extracting ./MNIST_data/train-labels-idx1-ubyte.gz\n",
      "Extracting ./MNIST_data/t10k-images-idx3-ubyte.gz\n",
      "Extracting ./MNIST_data/t10k-labels-idx1-ubyte.gz\n"
     ]
    }
   ],
   "source": [
    "# 导入数据\n",
    "mnist = input_data.read_data_sets('./MNIST_data', one_hot=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义权重和偏置初始化函数\n",
    "def weight_variable(shape):\n",
    "    \"\"\"\n",
    "    function:initialize the variable of the weight\n",
    "    parameter:shape:the shape of the weight\n",
    "    return:the variable of the weight\n",
    "    \"\"\"\n",
    "    initial = tf.truncated_normal(shape, stddev=0.1)\n",
    "    return tf.Variable(initial)\n",
    "def bias_variable(shape):\n",
    "    \"\"\"\n",
    "    function:initialize the variable of the bias\n",
    "    parameter:shape:the shape of the bias\n",
    "    return:the variable of the bias\n",
    "    \"\"\"\n",
    "    initial = tf.constant(0.1, shape=shape)\n",
    "    return tf.Variable(initial)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 卷积和池化操作层函数\n",
    "def conv2d(x, W):\n",
    "    \"\"\"\n",
    "    function:convolution function \n",
    "    param: x:input feature, W:the kernel of the convolution\n",
    "    return: the output of the convolution\n",
    "    \"\"\"\n",
    "    output = tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')\n",
    "    return output\n",
    "def max_pool_2x2(x):\n",
    "    \"\"\"\n",
    "    function: max pooling function\n",
    "    param: x:input feature\n",
    "    return: the output of the max pooling\n",
    "    \"\"\"\n",
    "    output = tf.nn.max_pool(x, ksize=[1, 2, 2,1], strides=[1, 2, 2, 1], padding='SAME')\n",
    "    return output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义输入特征及标签\n",
    "x = tf.placeholder(tf.float32, [None, 784])\n",
    "y_label = tf.placeholder(tf.float32, [None, 10])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 输入的[None,784]维度的数据reshape为[28,28]图片\n",
    "x_image = tf.reshape(x, [-1, 28, 28, 1])   # -1表示该维度的大小由其他维度大小决定"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 第一层卷积层\n",
    "W_conv1 = weight_variable([5, 5, 1, 32])\n",
    "b_conv1 = bias_variable([32])\n",
    "conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)\n",
    "pool1 = max_pool_2x2(conv1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 第二层卷积\n",
    "W_conv2 = weight_variable([5, 5, 32, 64])\n",
    "b_conv2 = bias_variable([64])\n",
    "conv2 = tf.nn.relu(conv2d(pool1, W_conv2) + b_conv2)\n",
    "pool2 = max_pool_2x2(conv2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 第三层全连接层\n",
    "pool2_flat = tf.reshape(pool2, [-1, 7*7*64])  # 将卷积层的特征图reshape为一维的向量\n",
    "W_fc1 = weight_variable([7*7*64, 1024])   # 输出的第一层全连接为1024维度\n",
    "b_fc1 = bias_variable([1024])\n",
    "fc1 = tf.nn.relu(tf.matmul(pool2_flat, W_fc1) + b_fc1)\n",
    "# 加入dropout防止过拟合\n",
    "keep_prob = tf.placeholder(tf.float32)     # drop out的概率\n",
    "fc1_drop =tf.nn.dropout(fc1, keep_prob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 第四层概率层\n",
    "W_fc2 = weight_variable([1024, 10])\n",
    "b_fc2 = bias_variable([10])\n",
    "fc2 = tf.nn.relu(tf.matmul(fc1_drop, W_fc2) + b_fc2)\n",
    "y_prob = tf.nn.softmax(fc2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "交叉熵损失函数:\n",
    "$Loss=-\\sum_{i=1}^ny_l*log(y_p)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义损失函数及优化方法\n",
    "cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_label*tf.log(y_prob), reduction_indices=[1]))\n",
    "# 设置网络训练参数及优化方法\n",
    "batch_size = 50\n",
    "num_iter = 2000\n",
    "train_lr = 1e-4\n",
    "train_step = tf.train.AdamOptimizer(train_lr).minimize(cross_entropy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算准确率\n",
    "correct_prediction = tf.equal(tf.argmax(y_prob, 1), tf.argmax(y_label, 1))\n",
    "accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建对话框并初始化全局变量\n",
    "sess = tf.InteractiveSession()\n",
    "tf.global_variables_initializer().run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train: step    0, loss 3.3311, accuracy 0.0400\n",
      "train: step  100, loss 2.3026, accuracy 0.1000\n",
      "train: step  200, loss 2.3026, accuracy 0.0400\n",
      "train: step  300, loss 2.1849, accuracy 0.1600\n",
      "train: step  400, loss 1.8502, accuracy 0.3200\n",
      "train: step  500, loss 1.9825, accuracy 0.2200\n",
      "train: step  600, loss 1.7481, accuracy 0.3400\n",
      "train: step  700, loss 1.5000, accuracy 0.5800\n",
      "train: step  800, loss 0.6155, accuracy 0.8400\n",
      "train: step  900, loss 0.1751, accuracy 0.9200\n",
      "train: step 1000, loss 0.1472, accuracy 0.9600\n",
      "train: step 1100, loss 0.1405, accuracy 0.9800\n",
      "train: step 1200, loss 0.0994, accuracy 0.9400\n",
      "train: step 1300, loss 0.0791, accuracy 0.9600\n",
      "train: step 1400, loss 0.0477, accuracy 1.0000\n",
      "train: step 1500, loss 0.2239, accuracy 0.9600\n",
      "train: step 1600, loss 0.0757, accuracy 0.9800\n",
      "train: step 1700, loss 0.0644, accuracy 0.9800\n",
      "train: step 1800, loss 0.0116, accuracy 1.0000\n",
      "train: step 1900, loss 0.0575, accuracy 0.9800\n",
      "the network has trained completely!\n"
     ]
    }
   ],
   "source": [
    "# 训练网络\n",
    "for i in range(num_iter):\n",
    "    batch = mnist.train.next_batch(batch_size) \n",
    "    train_step.run(feed_dict={x:batch[0], y_label:batch[1], keep_prob:0.5})\n",
    "    if i % 100 == 0:\n",
    "        train_loss,train_accuracy = sess.run((cross_entropy, accuracy), \\\n",
    "                                             feed_dict={x:batch[0], y_label:batch[1], keep_prob:1.0})\n",
    "        print('train: step %4d, loss %.4f, accuracy %.4f'% (i, train_loss, train_accuracy))\n",
    "print('the network has trained completely!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test accuracy 0.9600\n"
     ]
    }
   ],
   "source": [
    "print('test accuracy %.4f' % accuracy.eval(feed_dict={x:batch[0], y_label:batch[1], keep_prob:1.0}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
